package com.evil.application.service.impl;

import cn.hutool.core.collection.ListUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evil.account.api.RemoteUserEnterpriseService;
import com.evil.application.api.RemoteApplicationRobotService;
import com.evil.application.mapper.ApplicationInfoMapper;
import com.evil.application.mapper.ApplicationRobotMapper;
import com.evil.application.pojo.dto.robot.*;
import com.evil.application.pojo.entity.ApplicationInfo;
import com.evil.application.pojo.entity.ApplicationRobot;
import com.evil.application.service.ApplicationJudgeService;
import com.evil.application.service.ApplicationRobotRuleService;
import com.evil.application.service.ApplicationRobotService;
import com.evil.application.util.ApplicationUtil;
import com.evil.common.account.util.LoginUtil;
import com.evil.common.application.dto.judge.CopyJudgeReqDTO;
import com.evil.common.application.dto.judge.JudgeOr;
import com.evil.common.application.dto.judge.JudgesDTO;
import com.evil.common.application.dto.robot.RobotRuleDTO;
import com.evil.common.application.dto.robot.RobotSourceDTO;
import com.evil.common.application.dto.robot.rule.CopyRobotRuleReqDTO;
import com.evil.common.application.dto.robot.rule.RobotRulesDTO;
import com.evil.common.application.enums.ApplicationVersionEnum;
import com.evil.common.application.enums.judge.JudgeSourceTypeEnum;
import com.evil.common.application.enums.robot.RobotRuleSourceTypeEnum;
import com.evil.common.application.enums.robot.RobotSourceTypeEnum;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.StreamUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 应用机器人表
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@RestController
@AllArgsConstructor
public class ApplicationRobotServiceImpl extends ServiceImpl<ApplicationRobotMapper, ApplicationRobot> implements
        ApplicationRobotService, RemoteApplicationRobotService {

    private final static RobotSourceTypeEnum ROBOT_TYPE = RobotSourceTypeEnum.APPLICATION_ROBOT;
    private final static RobotRuleSourceTypeEnum ROBOT_RULE_TYPE = RobotRuleSourceTypeEnum.APPLICATION_ROBOT;
    private final static JudgeSourceTypeEnum DATA_RANGE = JudgeSourceTypeEnum.APPLICATION_ROBOT_DATA_RANGE;
    private final static JudgeSourceTypeEnum FILTER_CONDITION = JudgeSourceTypeEnum.APPLICATION_ROBOT_FILTER_CONDITION;

    private final LoginUtil loginUtil;

    private final ApplicationInfoMapper applicationInfoMapper;

    private final ApplicationJudgeService applicationJudgeService;

    private final ApplicationRobotRuleService applicationRobotRuleService;

    private final RemoteUserEnterpriseService remoteUserEnterpriseService;

    private final ApplicationLogServiceImpl logService;

    /**
     * 应用机器人列表
     *
     * @param applicationRobotsReqDTO applicationRobotsReqDTO
     * @return List
     */
    @Override
    public List<ApplicationRobotRespDTO> applicationRobots(ApplicationRobotsReqDTO applicationRobotsReqDTO) {
        String applicationKey = applicationRobotsReqDTO.getApplicationKey();
        Integer version = applicationRobotsReqDTO.getApplicationVersion();

        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(applicationKey);
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        // 机器人信息
        return this.perfectRobotInfo(this.baseMapper.findBySource(new RobotSourceDTO(ROBOT_TYPE, applicationInfo.getApplicationId(), version)));
    }

    /**
     * 应用机器人详情
     *
     * @param robotId robotId
     * @return ApplicationRobotRespDTO
     */
    @Override
    public ApplicationRobotRespDTO applicationRobotInfo(Long robotId) {
        ApplicationRobot robot = this.baseMapper.findById(robotId);
        // 检查企业
        loginUtil.checkEnterpriseId(robot.getEnterpriseId());

        return this.perfectRobotInfo(ListUtil.toList(robot)).stream()
                .findFirst().orElseThrow(() -> new BusinessException(RCodeEnum.ERROR_RUNTIME));
    }

    /**
     * 添加应用机器人
     *
     * @param addApplicationRobotReqDTO addApplicationRobotReqDTO
     */
    @Override
    public void addApplicationRobot(AddApplicationRobotReqDTO addApplicationRobotReqDTO) {
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        ApplicationInfo applicationInfo = applicationInfoMapper.findByKey(addApplicationRobotReqDTO.getApplicationKey());
        // 检查企业
        loginUtil.checkEnterpriseId(applicationInfo.getEnterpriseId());

        this.addRobot(addApplicationRobotReqDTO.toAddRobotReqDTO(applicationInfo.getApplicationId()));
    }

    /**
     * 编辑应用机器人
     *
     * @param modifyApplicationRobotReqDTO modifyApplicationRobotReqDTO
     */
    @Override
    public void modifyApplicationRobot(ModifyApplicationRobotReqDTO modifyApplicationRobotReqDTO) {
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        ApplicationRobot robot = this.baseMapper.findById(modifyApplicationRobotReqDTO.getRobotId());
        // 检查企业
        loginUtil.checkEnterpriseId(robot.getEnterpriseId());

        // 不允许编辑发布版
        if (ApplicationVersionEnum.RELEASE_VERSION.getId() == robot.getApplicationVersion()) {
            throw new BusinessException(RCodeEnum.ACCESS_DENIED);
        }

        modifyApplicationRobotReqDTO.copyProp(robot);
        logService.saveAndCheck(this.baseMapper, ApplicationRobot::getRobotId, robot);

        // 保存机器人信息
        this.saveRobotInfo(robot, modifyApplicationRobotReqDTO);
    }

    /**
     * 复制应用机器人
     *
     * @param robotId robotId
     */
    @Override
    public void copyApplicationRobot(Long robotId) {
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        ApplicationRobot robot = this.baseMapper.findById(robotId);
        // 检查企业
        loginUtil.checkEnterpriseId(robot.getEnterpriseId());

        // 复制机器人
        RobotSourceDTO targetSource = new RobotSourceDTO(robot.getSourceType(), robot.getSourceId(), robot.getApplicationVersion());
        this.copyRobot(robot, targetSource, new HashMap<>(), new HashMap<>(), null, false);
    }

    /**
     * 编辑应用机器人状态
     *
     * @param modifyAppRobotStatusReqDTO modifyAppRobotStatusReqDTO
     */
    @Override
    public void modifyApplicationRobotStatus(ModifyAppRobotStatusReqDTO modifyAppRobotStatusReqDTO) {
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        ApplicationRobot robot = this.baseMapper.findById(modifyAppRobotStatusReqDTO.getRobotId());
        // 检查企业
        loginUtil.checkEnterpriseId(robot.getEnterpriseId());

        robot.setRobotStatus(modifyAppRobotStatusReqDTO.getRobotStatus());
        logService.saveAndCheck(this.baseMapper, ApplicationRobot::getRobotId, robot);
    }

    /**
     * 删除机器人
     *
     * @param robotId robotId
     */
    @Override
    public void deleteApplicationRobot(Long robotId) {
        // 检查是否是管理员
        remoteUserEnterpriseService.checkEnterpriseManager(loginUtil.getLoginUserEnterprise());

        ApplicationRobot robot = this.baseMapper.findById(robotId);
        // 检查企业
        loginUtil.checkEnterpriseId(robot.getEnterpriseId());

        robot.setIsDeleted(SwitchEnum.YES.getId());
        logService.saveAndCheck(this.baseMapper, ApplicationRobot::getRobotId, robot);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addRobot(@Validated AddRobotReqDTO addRobotReqDTO) {
        ApplicationRobot robot = addRobotReqDTO.toApplicationRobot();
        robot.setEnterpriseId(loginUtil.getParamEnterpriseId(addRobotReqDTO.getEnterpriseId()));
        logService.saveAndCheck(this.baseMapper, ApplicationRobot::getRobotId, robot);

        // 保存机器人信息
        this.saveRobotInfo(robot, addRobotReqDTO);
    }

    /**
     * 发布应用机器人
     *
     * @param applicationInfo applicationInfo
     * @param controlIdMap    控件id替换映射
     * @param optionIdMap     选项id替换映射
     */
    @Override
    public void publishRobot(ApplicationInfo applicationInfo, Map<Long, Long> controlIdMap, Map<Long, Long> optionIdMap) {
        this.replaceRobot(applicationInfo, controlIdMap, optionIdMap, true);
    }

    /**
     * 还原应用机器人
     *
     * @param applicationInfo applicationInfo
     * @param controlIdMap    控件id替换映射
     * @param optionIdMap     选项id替换映射
     */
    @Override
    public void restoreRobot(ApplicationInfo applicationInfo, Map<Long, Long> controlIdMap, Map<Long, Long> optionIdMap) {
        this.replaceRobot(applicationInfo, controlIdMap, optionIdMap, false);
    }

    /**
     * 复制机器人
     *
     * @param copyRobotReqDTO copyRobotReqDTO
     */
    @Override
    public void copyRobot(CopyRobotReqDTO copyRobotReqDTO) {
        RobotSourceDTO source = copyRobotReqDTO.getSource();
        RobotSourceDTO target = copyRobotReqDTO.getTarget();
        Boolean updateSource = copyRobotReqDTO.getUpdateSource();
        Map<Long, Long> controlIdMap = copyRobotReqDTO.getControlIdMap();
        Map<Long, Long> optionIdMap = copyRobotReqDTO.getOptionIdMap();

        List<ApplicationRobot> sourceRobots = this.baseMapper.findBySource(source);
        List<ApplicationRobot> targetRobots = this.baseMapper.findBySource(target);

        Predicate<ApplicationRobot> filter = e -> !ApplicationUtil.DEFAULT_SOURCE_ROBOT_ID.equals(e.getSourceRobotId());
        Map<Long, ApplicationRobot> sourceRobotMap = StreamUtil.toMapFK(sourceRobots, filter, ApplicationRobot::getSourceRobotId);
        Map<Long, ApplicationRobot> targetRobotMap = StreamUtil.toMapFK(targetRobots, filter, ApplicationRobot::getSourceRobotId);

        // 新增、编辑
        sourceRobots.forEach(robot -> this.copyRobot(robot, target, targetRobotMap, controlIdMap, optionIdMap, updateSource));

        // 删除无效
        List<ApplicationRobot> deletedList = targetRobots.stream()
                .filter(e -> !sourceRobotMap.containsKey(e.getRobotId()))
                .peek(e -> e.setIsDeleted(SwitchEnum.YES.getId()))
                .collect(Collectors.toList());
        logService.saveAndCheck(this.baseMapper, ApplicationRobot::getRobotId, deletedList);
    }

    /**
     * 机器人信息
     *
     * @param robots robots
     * @return List
     */
    @Override
    public List<ApplicationRobotRespDTO> perfectRobotInfo(List<ApplicationRobot> robots) {
        List<Long> robotIds = StreamUtil.transListT(robots, ApplicationRobot::getRobotId);
        // 数据范围
        Map<Long, List<JudgeOr>> sourceDataRangeMap = applicationJudgeService.findMapBySourceIds(DATA_RANGE, robotIds);
        // 目标应用筛选条件
        Map<Long, List<JudgeOr>> sourceJudgesMap = applicationJudgeService.findMapBySourceIds(FILTER_CONDITION, robotIds);
        // 机器人规则
        Map<Long, List<RobotRuleDTO>> sourceRulesMap = applicationRobotRuleService.findMapBySourceIds(ROBOT_RULE_TYPE, robotIds);

        return robots.stream().map(e -> {
            ApplicationRobotRespDTO robotRespDTO = new ApplicationRobotRespDTO(e);
            // 筛选条件
            robotRespDTO.setRangeJudges(sourceDataRangeMap.getOrDefault(e.getRobotId(), new ArrayList<>()));
            // 目标应用筛选条件
            robotRespDTO.setJudgeOrs(sourceJudgesMap.getOrDefault(e.getRobotId(), new ArrayList<>()));
            // 机器人规则
            robotRespDTO.setRobotRules(sourceRulesMap.getOrDefault(e.getRobotId(), new ArrayList<>()));
            return robotRespDTO;
        }).collect(Collectors.toList());
    }

    /**
     * 保存机器人信息
     *
     * @param robot    robot
     * @param robotDTO robotDTO
     */
    private void saveRobotInfo(ApplicationRobot robot, ApplicationRobotDTO robotDTO) {
        // 数据范围
        applicationJudgeService.saveJudges(new JudgesDTO(DATA_RANGE, robot.getEnterpriseId(), robot.getRobotId(), robotDTO.getRangeJudges()));
        // 目标应用筛选条件
        applicationJudgeService.saveJudges(new JudgesDTO(FILTER_CONDITION, robot.getEnterpriseId(), robot.getRobotId(), robotDTO.getJudgeOrs()));
        // 保存机器人规则
        RobotRulesDTO robotRulesDTO = new RobotRulesDTO();
        robotRulesDTO.setEnterpriseId(robot.getEnterpriseId());
        robotRulesDTO.setSourceType(ROBOT_RULE_TYPE.getId());
        robotRulesDTO.setSourceId(robot.getRobotId());
        robotRulesDTO.setRobotRules(robotDTO.getRobotRules());
        robotRulesDTO.setTargetApplicationId(robot.getTargetApplicationId());
        applicationRobotRuleService.saveRobotRule(robotRulesDTO);
    }

    /**
     * 替换机器人信息
     *
     * @param applicationInfo applicationInfo
     * @param controlIdMap    控件id替换映射
     * @param isPublish       是否发布
     */
    private void replaceRobot(ApplicationInfo applicationInfo, Map<Long, Long> controlIdMap, Map<Long, Long> optionIdMap, boolean isPublish) {
        // 发布版
        int releaseVersion = ApplicationVersionEnum.RELEASE_VERSION.getId();
        // 设计版
        int designVersion = ApplicationVersionEnum.DESIGN_VERSION.getId();

        // 查询设计版的按钮列表
        RobotSourceDTO designSource = new RobotSourceDTO(ROBOT_TYPE, applicationInfo.getApplicationId(), designVersion);
        // 查询发布版的按钮列表
        RobotSourceDTO releaseSource = new RobotSourceDTO(ROBOT_TYPE, applicationInfo.getApplicationId(), releaseVersion);

        CopyRobotReqDTO copyRobotReqDTO = new CopyRobotReqDTO();
        copyRobotReqDTO.setUpdateSource(true);
        copyRobotReqDTO.setControlIdMap(controlIdMap);
        copyRobotReqDTO.setOptionIdMap(optionIdMap);
        if (isPublish) {
            // 发布
            copyRobotReqDTO.setSource(designSource);
            copyRobotReqDTO.setTarget(releaseSource);
        } else {
            // 还原
            copyRobotReqDTO.setSource(releaseSource);
            copyRobotReqDTO.setTarget(designSource);
        }
        // 复制
        this.copyRobot(copyRobotReqDTO);
    }

    /**
     * 复制机器人
     *
     * @param source         source 复制源
     * @param targetSource   目标源信息
     * @param targetRobotMap 目标原数据map
     * @param controlIdMap   控件id 替换映射
     * @param updateSource   是否更新源信息
     */
    private void copyRobot(ApplicationRobot source,
                           RobotSourceDTO targetSource,
                           Map<Long, ApplicationRobot> targetRobotMap,
                           Map<Long, Long> controlIdMap,
                           Map<Long, Long> optionIdMap,
                           boolean updateSource) {
        ApplicationRobot copyRobot;
        if (targetRobotMap.containsKey(source.getRobotId())) {
            copyRobot = targetRobotMap.get(source.getRobotId());
        } else {
            copyRobot = new ApplicationRobot();
            copyRobot.setSourceId(targetSource.getSourceId());
            copyRobot.setSourceType(targetSource.getSourceType());
            copyRobot.setApplicationVersion(targetSource.getVersion());
            copyRobot.setEnterpriseId(source.getEnterpriseId());
        }
        // 复制机器人属性
        this.copyProp(source, copyRobot, updateSource);
        logService.saveAndCheck(this.baseMapper, ApplicationRobot::getRobotId, copyRobot);

        // 更新源信息
        if (updateSource) {
            source.setSourceRobotId(copyRobot.getRobotId());
            logService.saveAndCheck(this.baseMapper, ApplicationRobot::getRobotId, source);
        }
        Long sourceId = source.getRobotId();
        Long targetId = copyRobot.getRobotId();
        Long enterpriseId = copyRobot.getEnterpriseId();

        // 保存筛选条件
        applicationJudgeService.copyJudges(new CopyJudgeReqDTO(DATA_RANGE, copyRobot.getEnterpriseId(), sourceId, targetId, controlIdMap, null, optionIdMap));
        // 目标应用筛选条件
        applicationJudgeService.copyJudges(new CopyJudgeReqDTO(FILTER_CONDITION, sourceId, targetId, null, controlIdMap, optionIdMap));
        // 保存机器人规则
        applicationRobotRuleService.copyRobotRule(new CopyRobotRuleReqDTO(ROBOT_RULE_TYPE, enterpriseId, sourceId, targetId, controlIdMap, updateSource));
    }

    /**
     * 复制机器人属性
     *
     * @param source source
     * @param target target
     */
    private void copyProp(ApplicationRobot source, ApplicationRobot target, boolean updateSource) {
        target.setRobotName(source.getRobotName());
        target.setFirstTriggerType(source.getFirstTriggerType());
        target.setTriggerSetting(source.getTriggerSetting());
        target.setRepeatSetting(source.getRepeatSetting());
        target.setTriggerType(source.getTriggerType());
        target.setTargetApplicationId(source.getTargetApplicationId());
        target.setRecalculation(source.getRecalculation());
        target.setRecalculationControlIds(source.getRecalculationControlIds());

        target.setRobotStatus(source.getRobotStatus());
        target.setSort(source.getSort());
        target.setSync(source.getSync());

        if (updateSource) {
            target.setSourceRobotId(source.getRobotId());
        }
    }
}
